import { ConnectionObserverAdaptor } from "../module/SdkModule"
import { DictArray } from "../utils/array.util"

class BridgeObserver {
  messageReceiver: MessengerFacade
  constructor(e: MessengerFacade) {
    this.messageReceiver = e
  }
  notify(e: messageBaseData) {
    this.messageReceiver.onMessageReceived(e)
  }
}
class ErrorMessageBridge {
  listen() {}
  stopListening() {}
  send() {
    throw Error("The sdk has been disconnected and can't make any new calls")
  }
}
class MessengerFacade {
  messenger: Messenger
  constructor(e: Messenger) {
    this.messenger = e
  }
  onMessageReceived(e: messageBaseData) {
    this.messenger.onMessageReceived(e)
  }
}
interface messageBaseData {
  fromId: number
  toId: number | number[]
  timestamp: number
  [key: string]: any
}
export class Messenger {
  sourceId: number
  targetId: number
  messageBridge: DirectMessageBridge | ErrorMessageBridge
  observers: DictArray
  constructor(e: number, t: number, i: DirectMessageBridge) {
    this.sourceId = e
    this.targetId = t
    this.messageBridge = i
    this.observers = new DictArray()
  }
  init() {
    const e = new MessengerFacade(this)
    const t = new BridgeObserver(e)
    this.messageBridge.listen(t)
  }
  dispose() {
    this.messageBridge.stopListening()
    this.messageBridge = new ErrorMessageBridge()
  }
  addObserver(e) {
    this.observers.add(e.messageType, e)
  }
  removeObserver(e) {
    this.observers.remove(e.messageType, e)
  }
  send(e: any) {
    const t = this.sourceId
    const i = this.targetId
    this.messageBridge.send(
      Object.assign(Object.assign({}, e), {
        fromId: t,
        toId: i,
        timestamp: Date.now()
      })
    )
  }
  onMessageReceived(e: messageBaseData) {
    if (!this.filterMessageId(e)) return
    const t = e.type
    const i = this.observers.getValuesAtKey(t)
    if (i) for (const t of i) t.notify(e.payload, e.timestamp)
  }
  filterMessageId(e: messageBaseData) {
    const t = e.toId,
      i = e.fromId
    return t instanceof Array ? t.indexOf(this.sourceId) > -1 : t === this.sourceId && i === this.targetId
  }
}
export class DirectMessageBridge {
  source: DirectMessageNode
  target: DirectMessageNode
  messageObserver: BridgeObserver
  constructor(e: DirectMessageNode, t) {
    ;(this.source = e), (this.target = t), (this.messageObserver = null)
  }
  listen(e: BridgeObserver) {
    this.messageObserver || ((this.messageObserver = e), this.source.addObserver(e))
  }
  stopListening() {
    this.messageObserver && this.source.removeObserver(this.messageObserver)
  }
  send(e) {
    const t = this.target
    t && t.post(Object.assign({}, e))
  }
}
export class DirectMessageNode {
  observers: BridgeObserver[]
  constructor() {
    this.observers = []
  }
  addObserver(e) {
    this.observers.push(e)
  }
  removeObserver(e: BridgeObserver) {
    const t = this.observers.indexOf(e)
    t > -1 && this.observers.splice(t, 1)
  }
  post(e: messageBaseData) {
    for (const t of this.observers) t.notify(e)
  }
}
const s = new Set()
s.add(0)
export class DirectMessageFactory {
  hostId: number
  host: DirectMessageNode
  onCreationObserver: ConnectionObserverAdaptor
  constructor(e: number, t: DirectMessageNode, i: ConnectionObserverAdaptor) {
    this.hostId = e
    this.host = t
    this.onCreationObserver = i
  }
  createMessenger() {
    const e: number = (() => {
      let e
      do {
        e = Math.floor(1000000 * Math.random())
      } while (s.has(e))
      s.add(e)
      return e
    })()
    const t = new DirectMessageNode()
    const i = new DirectMessageBridge(t, this.host)
    const n = new Messenger(e, this.hostId, i)
    this.onCreationObserver.notify(e, t, n)
    return n
  }
}
